/* Sokoban
 * HIT3243
 * Authors: Mike Blackney, Ben Ingarfield, Kurniawan
 */

#pragma once

#include "dxf.h"
#include "pawnlist.h"
#include <list>

/* class Level
 *
 * This class represents the tiles.
 * Responsible for knowing the stage of Qing the tiles
 * are in, of rendering the tiles and the sprites.
 *
 */

// Forward declarations:
class Pawn;
class CXFileEntity;

// The sounds that can be played:
#define SND_COLLIDE 1
#define SND_DEMO 2
#define SND_ENDLEVEL 3
#define SND_FALL 4
#define SND_FLOAT 5
#define SND_JUMP 6
#define SND_ENEMYJUMP 7


// The structure that defines our turn log, for UNDO and REDO actions:
struct Turn {
   Pawn* thePawn;
   int turnNo, pushNo;
   D3DXVECTOR3 location;
};
// Equality operator for the list functions:
int operator ==( const Turn &lhs, const Turn &rhs );


// The Level Class:
class Level {
public:
	static Level &Instance( );

	HRESULT Load( );
	void	Unload( );
	// PrepareX functions:
	// Int parameter sets the round.
	HRESULT PrepareGame( int );
	void PrepareTiles( );
	void PreparePlayer( );

	void PlaySound( int );

	void Update( double fTime, float fElapsedTime );
	void Render2D( double fTime, float fElapsedTime );
	void Render3D( double fTime, float fElapsedTime );

	virtual HRESULT OnCreateDevice( IDirect3DDevice9*, const D3DSURFACE_DESC* );
	HRESULT OnResetDevice( IDirect3DDevice9*, const D3DSURFACE_DESC* );
	void OnLostDevice( );

	bool TileIsEmpty( int x, int y ); // Returns true if the given x,y tile is empty.
	bool CanPushCrate( int x, int y, Pawn* pawn ); // Returns true if pawn can push a crate in (x,y)
	Pawn *GetCrate( int x, int y ); // Returns a crate if found at (x,y) or NULL

	void CheckLevelClear( ); // Checks to see if all target zones have a crate on them.
	void RegisterMovement( Pawn* );  // Adds a turn to the turn list and clears all later turns.
	void SetPlayerMoving( bool ); // Sets whether the player is moving

	int  GetCurrLevel( ) const { return iCurrLevel; } // Accessor

	D3DXVECTOR3 &AccessDesiredCameraPos( ) { return DesiredCameraPos; } // Accessor

	bool MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

private:
	// Can't instantiate out of class:
	Level( );
	~Level( );

	void ClearTiles( );
	void ClearPawns( );

	bool MoveCamera( float );
	void UpdateCameraHardware( );

	void ForcePlayer( const D3DXVECTOR3& ); // Force the player into a given position and reset AI.
	void PositionPawnsForCurrentTurn( );
	const Turn &FindLastPlayerTurn( );

	// Tile array
	struct TileInfo {
		int state; // The colour state of the tile
		Pawn *tile; // The tile spatial object
	} **tiles;

	bool bResetTiming, bPause, bNewLevel, bPlayerMoving;
	int iLevelWidth, iLevelHeight, iNumLevels, iCurrLevel, iNumTargets;
	double fLevelStartTime, fPauseTimer;

	// List of the pawns in the level:
	PawnList pawns;

	// A pointer to the player pawn for collision detection:
	Pawn *player;

	// A list of all the crates for collision detection:
	std::list<Pawn*> crates;

	dxf::Font font;

//	dxf::Sound sndCollide, sndDemo, sndEndLevel, sndFall, sndFloat, sndJump, sndEnemyJump;

	// The list of turns for Undo and Redo:
	std::list<Turn> undoList;

	// The pointer to our Direct3D device, used 
	// for the new 3d rendering:
	LPDIRECT3DDEVICE9 pd3dDevice;

	// This level state variable shows which state the level is in:
	enum LevelState {
		LS_Begin = 0,

		LS_PregamePause,
		LS_PregameZoom,
		LS_Game,
		LS_PostwinZoom,

		LS_End,
		LS_Invalid
	} mLevelState;

	D3DXVECTOR3 CameraPos, CameraLook, CameraNormal, 
		        DesiredCameraPos, DesiredCameraLook;

};
